package cn.xiaolang.function.java_gc;

import cn.xiaolang.common.utils.Monitor;
import lombok.extern.slf4j.Slf4j;

import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.util.concurrent.TimeUnit;

/**
 * 三 对象引用之-虚引用
 * <p>
 * 在任何时候都可能被垃圾回收器回收
 * <p>
 * 应用场景：
 * PhantomReference 主要用于辅助finalize方法。虚引用必须和引用队列(ReferenceQueue)联合使用。
 * 当垃圾回收器准备回收一个对象时，如果发现它还有虚引用，就会在回收对象的内存之前，把这个虚引用加入到与之关联的引用队列中。
 * <p>
 *
 * @author fuhw/Dean
 * @date 2020-05-14
 */
@Slf4j
public class PhantomReferenceStarted {


    public static void main(String[] args) throws InterruptedException {
        phantomReferenceSample();
    }

    private static void phantomReferenceSample() throws InterruptedException {
      
        System.gc();
        TimeUnit.SECONDS.sleep(3);

        Monitor.printlnMemory("1.原可用内存和总内存");
        // 强引用
        byte[] object = new byte[10 * Monitor.MB];
        Monitor.printlnMemory("2.实例化10M的数组后");
        // 建立虚引用
        ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
        PhantomReference<Object> phantomReference = new PhantomReference<>(object, referenceQueue);
        Monitor.printlnMemory("3.建立虚引用,不能通过get()方法获取到关联对象");
        System.out.println("phantomReference : " + phantomReference);
        System.out.println("phantomReference.get() : " + phantomReference.get());
        System.out.println("referenceQueue.poll() : " + referenceQueue.poll());

        // 断开object的强引用
        object = null;
        Monitor.printlnMemory("4.执行object=null;断开强引用后");

        // Notify GC
        System.gc();
        TimeUnit.SECONDS.sleep(3);

        Monitor.printlnMemory("5.GC后,加入引用队列但未回收object[]对象<===>");
        System.out.println("phantomReference : " + phantomReference);
        System.out.println("phantomReference.get() : " + phantomReference.get());
        System.out.println("referenceQueue.poll() : " + referenceQueue.poll());

        System.gc();
        //显示phantomReference = null;或者 phantomReference.clear(); 断开虚引用,关联对象变为不可达。可直接回收object[]对象
        // PS: 虚引用所引用对象不会被被释放直到所有指向该对象的虚引用被清除
        Monitor.printlnMemory("6.再次GC后,尝试但不一定回收object[]对象");
        System.out.println("phantomReference : " + phantomReference);
        System.out.println("phantomReference.get(): " + phantomReference.get());
        System.out.println("referenceQueue.poll() : " + referenceQueue.poll());
    }

}
